/*
 * EventService.java
 *
 * Created on May 13, 2009, 5:02 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package im.eventservice;

import im.clusterhead.ClusterHeadInfo;
import im.eventpackage.Event;
import im.eventpackage.EventHandler;
import im.filters.TopicEventFilter;
import im.filters.TypeEventFilter;
import im.general.BaseSuperAgent;
import im.general.Config;
import im.messages.PublishMessage;
import im.messages.SubscriptionMessage;
import im.node.MobileNodeInfo;
import im.numgenerator.PseudoRandomGenerator;
import im.resources.ResourceStatus;
import im.resources.model.EventServiceResourcesModel;
import im.resources.model.ResourcesDynamicUpdaterBehaviour;
import im.subscriptionpackage.Subscription;
import im.subscriptionpackage.SubscriptionHandler;
import im.subscriptionpackage.TopicSubscription;
import im.subscriptionpackage.TypeSubscription;
import jade.lang.acl.ACLMessage;
import jade.wrapper.StaleProxyException;
import java.lang.String;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author bluejack
 */
public class EventServiceAgent extends BaseSuperAgent{

    /* note: we should use the SINGLETON PATTERN */
    
    //getCHList()
    protected Hashtable<String,ClusterHeadInfo> heads;
    protected SubscriptionHandler subscriptionHandler;
    protected EventHandler eventHandler;
    protected ResourceStatus resourceStatus;

    //protected EventServiceResourcesModel resources_model;
    
    
    @Override
    public void setup() {
        try {
            super.setup();
            
            heads = new Hashtable();
            subscriptionHandler = new SubscriptionHandler();
            eventHandler = new EventHandler();

            addBehaviour(new PublishListenBehaviour(this));
            addBehaviour(new SubscribeListenBehaviour(this));
            addBehaviour(new RecipientsListenBehaviour(this));
            //addBehaviour(new UnsubscribeListenBehaviour(this));
            addBehaviour(new EventServiceLogBehaviour(this));

            /* generating resources */
            Object [] argv = this.getArguments();
            this.seedRand = ((Long)argv[1]).longValue();
            this.pr_gen = new PseudoRandomGenerator(this.seedRand);
            this.resources_model = new EventServiceResourcesModel(this.pr_gen);

             /* launching a new CH */
             Object [] argv2 = new Object[2];
             argv2[0] = new String(this.container);
            this.getContainerController().createNewAgent(Config.CH_ROOT_NAME, "im.clusterhead.ClusterHeadAgent", argv2).start();
            this.addHead(Config.CH_ROOT_NAME, new ClusterHeadInfo(Config.EVENT_SERVICE_NAME) );
            /* launched */

            //addBehaviour(new EventServiceListenBehaviour(this));
            addBehaviour(new SendCHListBehaviour(this));
            addBehaviour(new ClusterJoinNotifyBehaviour(this));
            addBehaviour(new ClusterShrinkNotifyBehaviour(this));
            addBehaviour(new NewCHListernerBehaviour(this));
            addBehaviour(new ResourcesDynamicUpdaterBehaviour(this));

        } catch (Exception ex) {
            Logger.getLogger(EventServiceAgent.class.getName()).log(Level.SEVERE, null, ex);
            ex.printStackTrace();
        }
    }
    
    
    public void addHead(String name, ClusterHeadInfo info) {
        heads.put(name, info);
    }

    public void updateHead(String name, ResourceStatus status ) {
        if( status == null )
                return;
        
        System.out.println("updateHead?" + name + " -> " + heads.toString());
        System.out.println( "   |-> " + ((ClusterHeadInfo)heads.get(name)).getGuestAgent() );
        System.out.println( "   |- " + status );

        String mn_id = ((ClusterHeadInfo)heads.get(name)).getGuestAgent();
        MobileNodeInfo mni = (MobileNodeInfo) nodes.get( mn_id );
        mni.setResourceStatus(status);
        nodes.put( mn_id, mni );
    }
    
    public void removeHead(String name) {
        heads.remove(name);
    }

    @Override
    public void updAttachedNodes() {
        //TODO set it better
        resources_model.setAttachedNodes(Config.MAX_CLUSTER_SIZE);
    }
    
    
    public Vector getCHList() {

        if( Config.CLUSTER_POLICY == Config.CLUSTER_HEURISTIC ) {
            updCHResourceStatus();
            Enumeration ks = heads.keys();
            double best_idx = -1.0;
            double curr_idx = -1.0;
            String best_ch = null, curr_ch;

            while(ks.hasMoreElements()){
                curr_ch = (String) ks.nextElement();
                curr_idx = ( (ClusterHeadInfo)heads.get(curr_ch) ).getResourceStatus().getStatusIndex();
                if( curr_idx > best_idx ) {
                    best_idx = curr_idx;
                    best_ch = curr_ch;
                }
            }
            Vector<String> res = new Vector<String>();
            res.add(best_ch);
            return res;
        }
        //updCHResourceStatus();
        return new Vector(heads.keySet());
    }

    protected void updCHResourceStatus() {
        Enumeration<ClusterHeadInfo> chi = heads.elements();
        ClusterHeadInfo i;
        while( chi.hasMoreElements()) {
            i = chi.nextElement();
            i.eraseAttachedNodes();
            if( i.getGuestAgent().equals(Config.EVENT_SERVICE_NAME))
                i.setResourceStatus(this.getResourceStatus());
            else {
                i.setResourceStatus(((MobileNodeInfo)nodes.get(i.getGuestAgent())).getResourceStatus());
            }
        }

        Enumeration<MobileNodeInfo> mni = nodes.elements();

        MobileNodeInfo i2;
        while( mni.hasMoreElements()) {
            i2 = mni.nextElement();
            i = heads.get(i2.getCh_name());
            i.addAttachedNode();
            heads.put(i2.getCh_name(),i);
        }
    }
    
    protected void subscribe(String subscriber, SubscriptionMessage subscriptionMessage) {
        if(subscriptionMessage!=null)
            subscriptionHandler.subscribe(subscriber, subscriptionMessage.getSubscription());
    }
    
    protected void unsubscribe(String subscriber, SubscriptionMessage subscriptionMessage) {
        if(subscriptionMessage!=null)
            subscriptionHandler.unsubscribe(subscriber, subscriptionMessage.getSubscription());
    }

    void publish(PublishMessage publishMessage) {
        if(publishMessage==null)
            return;
            
        Event e = publishMessage.getEvent();
        eventHandler.publish(e);
        
        TypeEventFilter typeEventFilter = new TypeEventFilter();
        TopicEventFilter topicEventFilter = new TopicEventFilter();
        
        Enumeration subscriptions = subscriptionHandler.getSubscriptions();
        
        Subscription subscription = null;
        
        while(subscriptions.hasMoreElements()) {
            subscription = (Subscription) subscriptions.nextElement();
            if(subscription instanceof im.subscriptionpackage.TopicSubscription)
                if(topicEventFilter.filterSubscription(e,(TopicSubscription) subscription))
                    eventHandler.addRecipients(e, subscriptionHandler.getSubscribers(subscription));

            if(subscription instanceof im.subscriptionpackage.TypeSubscription)
                if(typeEventFilter.filterSubscription(e,(TypeSubscription) subscription))
                    eventHandler.addRecipients(e, subscriptionHandler.getSubscribers(subscription));
        }

        addBehaviour(new NotifyBehaviour(this, e, eventHandler));
    }

    public ResourceStatus getResourceStatus() {
        resourceStatus = new ResourceStatus( this.resources_model);
        return resourceStatus;
    }

}
